package com.gjt.serverSocket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * Created with Intel IDEA.
 * echo 服务器：客户端发送什么服务器会送什么 并持续到客户端结束
 * ServerSocket 的无参构造函数更好；需要在构造ServerSocket对象之后绑定socket地址
 *    ServerSocket server = new ServerSocket();
 *    try{
 *        SocketServer address =new  InetSocketAddress(port);
 *        server.bind(address);
 *    }finally{
 *        try{
 *            server.close();
 *        }catch(){}
 *    }
 *
 *
 * java 7 中ServerSocket实现AutoCloseable,
 *    try(SocketServer address = new ServerSocket(port)){
 *
 *    }
 *    关闭服务器socket之后 ，不能重新连接，即使同一个端口也不可以，再次调用isClose()返回true;
 *   而对于无参构造的ServerSocket 而且还没有绑定端口的，并不认为它是关闭的；返回false
 *   isBound()可以查看ServerSocket 是否已经绑定接口；
 *
 *   note: 如果要测试ServerSocket是否打开，就必须同时isBound()为true 和 isClose()为false;
 *
 * @Auther: gjt
 * @Date: 2024/5/5 14:59
 */
public class EchoServer {
    public final static int DEFAULT_PORT = 7;

    public static void main(String[] args) {
        int port;
        try{
            port = Integer.parseInt(args[0]);
        }catch (RuntimeException e){
            port = DEFAULT_PORT;
        }
        System.out.println("Listening for connections on port " + port);
        ServerSocketChannel serverChannel;
        Selector selector;
        try{
            // 获取通道
           serverChannel = ServerSocketChannel.open();
           // 在通道上获取socket
           ServerSocket socket = serverChannel.socket();
           InetSocketAddress address = new InetSocketAddress(port);

           // 通道和socket 绑定
           socket.bind(address);
           // 设置通道不阻塞
           serverChannel.configureBlocking(false);

           selector = Selector.open();
           serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        }catch (IOException e){
            e.printStackTrace();
            return;
        }
        while(true){
            try{
                selector.select();
            }catch (IOException e){
                e.printStackTrace();
                break;
            }
            Set<SelectionKey> readKey = selector.selectedKeys();
            Iterator<SelectionKey> iterator = readKey.iterator();

            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                iterator.remove();
                try{
                    if(key.isAcceptable()){
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();
                        SocketChannel client = server.accept();
                        System.out.println("Accepted connection from "+ client);
                        client.configureBlocking(false);
                        SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
                        ByteBuffer buffer = ByteBuffer.allocate(100);
                        clientKey.attach(buffer);
                    }
                    if(key.isReadable()){
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        client.read(output);
                    }

                    if(key.isWritable()){
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        output.flip();
                        client.write(output);
                        output.compact();
                    }
                }catch (IOException e){
                    key.channel();
                    try{
                      key.channel().close();
                    } catch (IOException ignored){}
                }
            }
        }

    }


}
